namespace NFTRedit
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Drawing;
    using System.IO;
    using System.Text;

    public class Font
    {
        private static readonly uint MAGIC = 0x4e465452;
        private ushort mByteOrderMark;
        private CMAPChunk[] mCharacterMaps;
        private Character[] mCharacters;
        private Dictionary<ushort, Character> mCodeToTile;
        private FINFChunk mFileInfo;
        private uint mFileSize;
        private CWDHChunk mGlyphDimensions;
        private Dictionary<ushort, Character> mIndexToTile;
        private ushort mNumChunks;
        private ushort mSize;
        private CGLPChunk mTileGraphics;
        private ushort mUnknown;

        public Font(BinaryReader reader)
        {
            this.Load(reader);
        }

        private void AddAllTiles(ArrayList characterList, CMAPChunk map)
        {
            Encoding.GetEncoding("Shift_JIS");
            foreach (KeyValuePair<ushort, ushort> pair in map.Mappings)
            {
                if (pair.Value <= this.Tiles.Length)
                {
                    Character character = new Character(this.Tiles[pair.Value], pair.Key, ' ', this.Properties[pair.Value][0], this.Properties[pair.Value][1], this.Properties[pair.Value][2], this.MaxHeight);
                    characterList.Add(character);
                    this.mIndexToTile.Add(pair.Value, character);
                    if (this.mCodeToTile.ContainsKey(pair.Key))
                    {
                        throw new FormatException("Duplicate tile code.");
                    }
                    this.mCodeToTile.Add(pair.Key, character);
                }
            }
        }

        private void DumpCharacterMap()
        {
            FileStream stream = new FileStream("maps.txt", FileMode.Create, FileAccess.Write);
            StreamWriter writer = new StreamWriter(stream);
            for (int i = 0; i < this.mCharacters.Length; i++)
            {
                writer.WriteLine(string.Format("{0:x4}", this.mCharacters[i].Code));
            }
            writer.Close();
        }

        public Character GetTileByCode(ushort code)
        {
            Character character = null;
            if ((this.mCodeToTile != null) && this.mCodeToTile.TryGetValue(code, out character))
            {
                return character;
            }
            return null;
        }

        public Character GetTileByIndex(ushort index)
        {
            Character character = null;
            if ((this.mIndexToTile != null) && this.mIndexToTile.TryGetValue(index, out character))
            {
                return character;
            }
            return null;
        }

        private void Load(BinaryReader reader)
        {
            this.ReadHeader(reader);
            this.mFileInfo = new FINFChunk(reader);
            this.mTileGraphics = new CGLPChunk(reader);
            int firstCwdhOffset = this.mFileInfo.FirstCwdhOffset;
            int firstCmapOffset = this.mFileInfo.FirstCmapOffset;
            if (firstCwdhOffset >= this.mFileSize)
            {
                throw new FormatException("CWDH offset " + string.Format("0x{0:x4}", firstCwdhOffset) + " points past end of file.");
            }
            if (firstCmapOffset >= this.mFileSize)
            {
                throw new FormatException("CMAP offset " + string.Format("0x{0:x4}", firstCmapOffset) + " points past end of file.");
            }
            if (firstCwdhOffset != reader.BaseStream.Position)
            {
                throw new FormatException("CWDH offset " + string.Format("0x{0:x4}", firstCwdhOffset) + " is not at end of CGLP chunk.");
            }
            this.mGlyphDimensions = new CWDHChunk(reader);
            if (firstCmapOffset != reader.BaseStream.Position)
            {
                int length = this.mGlyphDimensions.Length;
                long position = reader.BaseStream.Position;
                if ((firstCwdhOffset + this.mGlyphDimensions.Length) != firstCmapOffset)
                {
                    throw new FormatException("CMAP offset " + string.Format("0x{0:x4}", firstCmapOffset) + " is not at end of CWDH chunk.");
                }
                reader.BaseStream.Seek((long) firstCmapOffset, SeekOrigin.Begin);
            }
            if (this.Tiles.Length > this.Properties.Length)
            {
                this.mTileGraphics.CorrectTileCount(this.Properties.Length);
            }
            ArrayList characterList = new ArrayList();
            int num3 = this.mNumChunks - 3;
            this.mCharacterMaps = new CMAPChunk[num3];
            this.mIndexToTile = new Dictionary<ushort, Character>();
            this.mCodeToTile = new Dictionary<ushort, Character>();
            for (int i = 0; i < num3; i++)
            {
                if (firstCmapOffset == 0)
                {
                    throw new FormatException("Ran out of chunks.");
                }
                if (firstCmapOffset >= this.mFileSize)
                {
                    throw new FormatException("CMAP offset " + string.Format("0x{0:x4}", firstCmapOffset) + " points past end of file.");
                }
                if (firstCmapOffset != reader.BaseStream.Position)
                {
                    throw new FormatException("CMAP offset " + string.Format("0x{0:x4}", firstCmapOffset) + " is not at end of previous chunk.");
                }
                this.mCharacterMaps[i] = new CMAPChunk(reader);
                this.AddAllTiles(characterList, this.mCharacterMaps[i]);
                firstCmapOffset = this.mCharacterMaps[i].NextCmapOffset;
            }
            this.mCharacters = (Character[]) characterList.ToArray(typeof(Character));
        }

        private void ReadHeader(BinaryReader reader)
        {
            if (reader.ReadUInt32() != MAGIC)
            {
                throw new FormatException("File is not NFTR format.");
            }
            this.mByteOrderMark = reader.ReadUInt16();
            if (this.mByteOrderMark != 0xfeff)
            {
                throw new FormatException("Unsupported byte order mark.\nPlease send me your font file so\nI can add support for it.");
            }
            this.mUnknown = reader.ReadUInt16();
            this.mFileSize = reader.ReadUInt32();
            this.mSize = reader.ReadUInt16();
            this.mNumChunks = reader.ReadUInt16();
        }

        public void RecalculateFileSize()
        {
            this.mTileGraphics.RecalculateSize();
            this.mGlyphDimensions.RecalculateSize();
            int num = (this.mSize + this.mFileInfo.Length) + this.mTileGraphics.Length;
            this.mFileInfo.FirstCwdhOffset = num;
            num += this.mGlyphDimensions.Length;
            this.mFileInfo.FirstCmapOffset = num;
            for (int i = 0; i < this.mCharacterMaps.Length; i++)
            {
                this.mCharacterMaps[i].RecalculateSize();
                num += this.mCharacterMaps[i].Length;
            }
            this.mFileSize = (uint) num;
        }

        public void ResizeTiles(byte w, byte h)
        {
            this.mTileGraphics.ResizeTiles(w, h);
            for (int i = 0; i < this.Tiles.Length; i++)
            {
                this.GetTileByIndex((ushort) i).Tile = this.Tiles[i];
            }
            this.RecalculateFileSize();
        }

        public void Save(BinaryWriter writer)
        {
            this.RecalculateFileSize();
            this.WriteHeader(writer);
            this.mFileInfo.Save(writer);
            this.mTileGraphics.Save(writer);
            int firstCwdhOffset = this.mFileInfo.FirstCwdhOffset;
            int firstCmapOffset = this.mFileInfo.FirstCmapOffset;
            while (writer.BaseStream.Position < firstCwdhOffset)
            {
                writer.Write((byte) 0);
            }
            this.mGlyphDimensions.Save(writer);
            int num3 = this.mNumChunks - 3;
            while (writer.BaseStream.Position < firstCmapOffset)
            {
                writer.Write((byte) 0);
            }
            writer.Flush();
            for (int i = 0; i < num3; i++)
            {
                this.mCharacterMaps[i].Save(writer, (i + 1) == num3);
            }
        }

        private void WriteHeader(BinaryWriter writer)
        {
            writer.Write(MAGIC);
            writer.Write(this.mByteOrderMark);
            writer.Write(this.mUnknown);
            writer.Write(this.mFileSize);
            writer.Write(this.mSize);
            writer.Write(this.mNumChunks);
        }

        public ushort BPP
        {
            get
            {
                return this.mTileGraphics.BPP;
            }
        }

        public Bitmap[] Dots
        {
            get
            {
                return this.mTileGraphics.Dots;
            }
        }

        public int Length
        {
            get
            {
                return (int) this.mFileSize;
            }
        }

        public byte MaxHeight
        {
            get
            {
                return this.mTileGraphics.MaxHeight;
            }
        }

        public byte MaxWidth
        {
            get
            {
                return this.mTileGraphics.MaxWidth;
            }
        }

        public byte Orientation
        {
            get
            {
                return this.mTileGraphics.Orientation;
            }
        }

        public byte[][] Properties
        {
            get
            {
                return this.mGlyphDimensions.Properties;
            }
        }

        public Bitmap[] Tiles
        {
            get
            {
                return this.mTileGraphics.Tiles;
            }
        }
    }
}

